#include "SFML2DRenderEngine.h"
#include "..\Managers\SFMLSpriteManager.h"
#include "..\Generic\Convert.h"
#include "..\Debug\DebugLog.h"
#include "..\Managers\StateManager.h"
#include "..\Managers\PhysicsManager.h"
#include "..\Debug\DebugDraw.h"

using namespace vn;

SFML2DRenderEngine::SFML2DRenderEngine(unsigned int p_views) : m_currentRenderState(RENDER_STATE_WINDOW), m_isInitialized(false)
{
	m_spriteManager = new SFMLSpriteManager(p_views);
}

SFML2DRenderEngine::~SFML2DRenderEngine(void)
{
	if (m_spriteManager != nullptr)
	{
		delete m_spriteManager;
		m_spriteManager = nullptr;
	}
	clearCameras();
	m_window.close();
}

void SFML2DRenderEngine::initialize()
{
	if (!m_isInitialized)
	{
		IniFile iniFile("config.ini");
		if (!iniFile.parse())
			recreateIniFile(iniFile);

		int flags = sf::Style::Close;

		unsigned int screenWidth = stringToNumber<unsigned int>(iniFile.getValue("Video", "Width", "1280"));
		unsigned int screenHeight = stringToNumber<unsigned int>(iniFile.getValue("Video", "Height", "720"));
		bool vsync = stringToBool(iniFile.getValue("Video", "Vsync", "false"));
		bool fullscreen = stringToBool(iniFile.getValue("Video", "Fullscreen", "true"));

		if (fullscreen)
			flags |= sf::Style::Fullscreen;

		sf::VideoMode videoMode;
		videoMode.bitsPerPixel = 32;
		videoMode.width = screenWidth;
		videoMode.height = screenHeight;
		testVideoMode(videoMode);
		m_window.create(videoMode, "Rockman! Rockman!", flags);
		m_window.setFramerateLimit(60);
		PhysicsManager::getSingleton().setDebugDraw(new DebugDraw(&m_window));
		m_isInitialized = true;
	}
	else
	{
		Debug::log("WARNING: System is trying to initialize render engine more than once.");
	}
}

vn::Sprite* SFML2DRenderEngine::createSprite(unsigned int p_view, const std::string& p_id, float p_layer)
{
	return m_spriteManager->createSprite(p_view, p_id, p_layer);
}

vn::Text* SFML2DRenderEngine::createText(unsigned int p_view, const std::string& p_fontID, const std::string& p_string, float p_layer)
{
	return m_spriteManager->createText(p_view, p_fontID, p_string, p_layer);
}

vn::TileEngine* SFML2DRenderEngine::createTiles(unsigned int p_view, const std::string& p_filepath, float p_layer)
{
	return m_spriteManager->createTiles(p_view, p_filepath, p_layer);
}

vn::Camera* SFML2DRenderEngine::createCamera(unsigned int p_view, const std::string& p_cameraID)
{
	auto it = m_cameras.find(p_cameraID);
	if (it == m_cameras.end())
	{
		vn::SFMLCamera* camera = new vn::SFMLCamera(m_spriteManager->getView(p_view));
		m_cameras.insert(std::make_pair(p_cameraID, camera));
		return camera;
	}
	else
		Debug::log("WARNING: Camera ID " + p_cameraID + " already exists. Camera creation failed.");
	
	return nullptr;
}

vn::RectangleShape* SFML2DRenderEngine::createRectangleShape(unsigned int p_view, const vn::Vector2& p_size, float p_layer)
{
	return m_spriteManager->createRectangleShape(p_view, p_size, p_layer);
}

void SFML2DRenderEngine::pollEvents()
{
	sf::Event event;
	while (m_window.pollEvent(event))
	{
		switch (event.type)
		{
		case sf::Event::Closed:
			StateManager::getSingleton().shutdown();
				break;
		default:
			break;
		}
	}
}

void SFML2DRenderEngine::beginRender()
{
	pollEvents();
	switch (m_currentRenderState)
	{
	case RENDER_STATE_WINDOW:
		{
			m_window.clear(sf::Color::Black);
			break;
		}
	case RENDER_STATE_TEXTURE:
		{
			m_renderTexture.clear(sf::Color::Black);
			break;
		}
	}
}

void SFML2DRenderEngine::render()
{
	if (!m_cameras.empty())
	{
		for (auto it = m_cameras.begin(); it != m_cameras.end(); it++)
			(*it).second->update();
	}

	switch (m_currentRenderState)
	{
	case RENDER_STATE_WINDOW:
		{
			m_spriteManager->drawSprites(m_window);
			break;
		}
	case RENDER_STATE_TEXTURE:
		{
			m_spriteManager->drawSprites(m_renderTexture);
			break;
		}
	default:
		break;
	}
}

void SFML2DRenderEngine::endRender()
{
	switch (m_currentRenderState)
	{
	case RENDER_STATE_WINDOW:
		{
			m_window.display();
			break;
		}
	case RENDER_STATE_TEXTURE:
		{
			m_renderTexture.display();
			m_window.clear();
			sf::Sprite sprite(m_renderTexture.getTexture());
			m_window.draw(sprite);
			m_window.display();
			break;
		}
	}
}

void SFML2DRenderEngine::recreateIniFile(IniFile& p_iniFile)
{
	Debug::log("WARNING: Recreating IniFile with default values");
	sf::VideoMode videoMode;
	testVideoMode(videoMode);
	p_iniFile.addModify("Video", "Width", numberToString(videoMode.width));
	p_iniFile.addModify("Video", "Height", numberToString(videoMode.height));
	p_iniFile.addModify("Video", "Fullscreen", "true");
	p_iniFile.addModify("Video", "Vsync", "false");
	p_iniFile.addModify("General", "ShowCursor", "false");
	p_iniFile.addModify("General", "StartState", "IntroState");
	p_iniFile.save();
}

void SFML2DRenderEngine::testVideoMode(sf::VideoMode& p_videoMode)
{
	if (!p_videoMode.isValid())	// Is only testing full screen resolutions
	{
		Debug::log("WARNING: resolution " + numberToString(p_videoMode.width) + "x" + numberToString(p_videoMode.height) + " is invalid.");
		p_videoMode = *sf::VideoMode::getFullscreenModes().begin();
	}
}

void SFML2DRenderEngine::destroySprite(vn::Sprite* p_sprite)
{
	m_spriteManager->destroySprite(static_cast<vn::SFMLSprite*>(p_sprite));
}

void SFML2DRenderEngine::destroyTileEngine(vn::TileEngine* p_tileEngine)
{
	m_spriteManager->destroyTileEngine(static_cast<vn::SFMLTileEngine*>(p_tileEngine));
}

void SFML2DRenderEngine::destroyCamera(vn::Camera* p_camera)
{
	for (auto it = m_cameras.begin(); it != m_cameras.end(); it++)
	{
		if ((*it).second == p_camera)
		{
			delete (*it).second;
			(*it).second = nullptr;
			m_cameras.erase(it);
			break;
		}
	}
}

void SFML2DRenderEngine::destroyText(vn::Text* p_text)
{
	m_spriteManager->destroyText(static_cast<SFMLText*>(p_text));
}

void SFML2DRenderEngine::destroyRectangleShape(vn::RectangleShape* p_rectangleShape)
{
	m_spriteManager->destroyRectangleShape(static_cast<SFMLRectangleShape*>(p_rectangleShape));
}

inline void SFML2DRenderEngine::clearCameras()
{
	if (!m_cameras.empty())
	{
		for (auto it = m_cameras.begin(); it != m_cameras.end(); it++)
		{
			delete (*it).second;
			(*it).second = nullptr;
		}
		m_cameras.clear();
	}
}

vn::Camera* SFML2DRenderEngine::getCamera(const std::string& p_cameraID)
{
	auto it = m_cameras.find(p_cameraID);
	if (it != m_cameras.end())
		return it->second;
	return nullptr;
}

void SFML2DRenderEngine::destroyAllDrawables()
{
	m_spriteManager->clearAllDrawables();
}

void SFML2DRenderEngine::clearAll()
{
	m_spriteManager->clearAllDrawables();
	clearCameras();
}

void SFML2DRenderEngine::setView(unsigned int p_view)
{
	switch (m_currentRenderState)
	{
	case RENDER_STATE_WINDOW:
		m_spriteManager->setView(p_view, m_window);
		break;
	case RENDER_STATE_TEXTURE:
		m_spriteManager->setView(p_view, m_window);
		break;
	default:
		break;
	}
}